# Load in the whole Alzheimers data file
data <- read.csv('~/repos/portfolio/demonstrative/R/datasets/alzheimers/alzheimers.csv')
# Remove this field ... I forgot to do that when creating the dataset
data <- data %>% select(-male)
# Normalize gender labels
stopifnot(all(!is.na(data$gender)))
normalize.gender <- function(x) {
  is.male <- x %>% tolower %>% str_detect('^m')
  ifelse(is.male, 'Male', 'Female') %>% factor
}
data <- data %>% mutate(gender=normalize.gender(gender))
# Convert integer fields to numeric for the sake of consistency
data <- data %>% mutate_each_(funs(as.numeric), c('Betacellulin', 'Eotaxin_3'))
head(data)
X <- data %>% select(-response)
y <- data[,'response']
table(y)
y
   Impaired NotImpaired 
         91         242 
names(X)
  [1] "ACE_CD143_Angiotensin_Converti"   "ACTH_Adrenocorticotropic_Hormon" 
  [3] "AXL"                              "Adiponectin"                     
  [5] "Alpha_1_Antichymotrypsin"         "Alpha_1_Antitrypsin"             
  [7] "Alpha_1_Microglobulin"            "Alpha_2_Macroglobulin"           
  [9] "Angiopoietin_2_ANG_2"             "Angiotensinogen"                 
 [11] "Apolipoprotein_A_IV"              "Apolipoprotein_A1"               
 [13] "Apolipoprotein_A2"                "Apolipoprotein_B"                
 [15] "Apolipoprotein_CI"                "Apolipoprotein_CIII"             
 [17] "Apolipoprotein_D"                 "Apolipoprotein_E"                
 [19] "Apolipoprotein_H"                 "B_Lymphocyte_Chemoattractant_BL" 
 [21] "BMP_6"                            "Beta_2_Microglobulin"            
 [23] "Betacellulin"                     "C_Reactive_Protein"              
 [25] "CD40"                             "CD5L"                            
 [27] "Calbindin"                        "Calcitonin"                      
 [29] "CgA"                              "Clusterin_Apo_J"                 
 [31] "Complement_3"                     "Complement_Factor_H"             
 [33] "Connective_Tissue_Growth_Factor"  "Cortisol"                        
 [35] "Creatine_Kinase_MB"               "Cystatin_C"                      
 [37] "EGF_R"                            "EN_RAGE"                         
 [39] "ENA_78"                           "Eotaxin_3"                       
 [41] "FAS"                              "FSH_Follicle_Stimulation_Hormon" 
 [43] "Fas_Ligand"                       "Fatty_Acid_Binding_Protein"      
 [45] "Ferritin"                         "Fetuin_A"                        
 [47] "Fibrinogen"                       "GRO_alpha"                       
 [49] "Gamma_Interferon_induced_Monokin" "Glutathione_S_Transferase_alpha" 
 [51] "HB_EGF"                           "HCC_4"                           
 [53] "Hepatocyte_Growth_Factor_HGF"     "I_309"                           
 [55] "ICAM_1"                           "IGF_BP_2"                        
 [57] "IL_11"                            "IL_13"                           
 [59] "IL_16"                            "IL_17E"                          
 [61] "IL_1alpha"                        "IL_3"                            
 [63] "IL_4"                             "IL_5"                            
 [65] "IL_6"                             "IL_6_Receptor"                   
 [67] "IL_7"                             "IL_8"                            
 [69] "IP_10_Inducible_Protein_10"       "IgA"                             
 [71] "Insulin"                          "Kidney_Injury_Molecule_1_KIM_1"  
 [73] "LOX_1"                            "Leptin"                          
 [75] "Lipoprotein_a"                    "MCP_1"                           
 [77] "MCP_2"                            "MIF"                             
 [79] "MIP_1alpha"                       "MIP_1beta"                       
 [81] "MMP_2"                            "MMP_3"                           
 [83] "MMP10"                            "MMP7"                            
 [85] "Myoglobin"                        "NT_proBNP"                       
 [87] "NrCAM"                            "Osteopontin"                     
 [89] "PAI_1"                            "PAPP_A"                          
 [91] "PLGF"                             "PYY"                             
 [93] "Pancreatic_polypeptide"           "Prolactin"                       
 [95] "Prostatic_Acid_Phosphatase"       "Protein_S"                       
 [97] "Pulmonary_and_Activation_Regulat" "RANTES"                          
 [99] "Resistin"                         "S100b"                           
[101] "SGOT"                             "SHBG"                            
[103] "SOD"                              "Serum_Amyloid_P"                 
[105] "Sortilin"                         "Stem_Cell_Factor"                
[107] "TGF_alpha"                        "TIMP_1"                          
[109] "TNF_RII"                          "TRAIL_R3"                        
[111] "TTR_prealbumin"                   "Tamm_Horsfall_Protein_THP"       
[113] "Thrombomodulin"                   "Thrombopoietin"                  
[115] "Thymus_Expressed_Chemokine_TECK"  "Thyroid_Stimulating_Hormone"     
[117] "Thyroxine_Binding_Globulin"       "Tissue_Factor"                   
[119] "Transferrin"                      "Trefoil_Factor_3_TFF3"           
[121] "VCAM_1"                           "VEGF"                            
[123] "Vitronectin"                      "von_Willebrand_Factor"           
[125] "age"                              "tau"                             
[127] "p_tau"                            "Ab_42"                           
[129] "Genotype"                         "gender"                          
X %>% sapply(class) %>% table
.
 factor numeric 
      2     128 
#X %>% sapply(class) %>% .[. == 'factor']
names(X)[sapply(X, class) == 'factor']
[1] "Genotype" "gender"  

Partial Analysis

We could start by looking at the relationship between some of the more intuitive variables like Age, Gender, and Genotype and impairment, to see if there are any obvious relationships.

Age vs Impairment

data %>% 
  mutate(age_range=cut(age, breaks=5)) %>%
  group_by(age_range, response) %>% tally %>% 
  plot_ly(x=~age_range, y=~n, color=~response, type='bar') %>%
  plotly::layout(hovermode='closest')
minimal value for n is 3, returning requested palette with 3 different levels
minimal value for n is 3, returning requested palette with 3 different levels

Genotype vs Impairment

data %>% 
  group_by(Genotype, response) %>% tally %>% 
  plot_ly(x=~Genotype, y=~n, color=~response, type='bar') %>%
  plotly::layout(hovermode='closest')
minimal value for n is 3, returning requested palette with 3 different levels
minimal value for n is 3, returning requested palette with 3 different levels

Gender vs Impairment

data %>% 
  group_by(gender, response) %>% tally %>% 
  plot_ly(x=~gender, y=~n, color=~response, type='bar') %>%
  plotly::layout(hovermode='closest')
minimal value for n is 3, returning requested palette with 3 different levels
minimal value for n is 3, returning requested palette with 3 different levels

{r} # data %>% plot_ly(x=~gender, y=~age, type='box') #

Protein Analysis

Now what about the other ~125 variables? We can’t look at them all one at a time so perhaps there is a way to “condense” them into something more manageable:

library(corrplot)
d.pca <- X %>% select(-gender, -Genotype)
cor.mat <- corrplot(cor(d.pca), order='hclust', tl.col='black', tl.cex=.5)

# Extract the variable names from the figure below since they will be useful
# for creating similar plots for comparison (and it's easier to compare in the same order)
cor.var <- rownames(cor.mat)
#d.pca <- X %>% select(-gender, -Genotype)
pca <- prcomp(d.pca, scale=T)
corrplot(cor(d.pca[,cor.var], pca$x[,1:25]), tl.col='black', tl.cex=.5)

d.pca.pred <- as.data.frame(predict(pca, d.pca)[,1:2])
d.pca.pred$response <- y
pca.vars <- c('tau', 'Cortisol', 'age', 'Apolipoprotein_H')
d.pca.var <- pca$rotation[,2:3] %>% 
  as.data.frame %>% setNames(., c('PC1', 'PC2')) %>%
  add_rownames(var='variable') %>%
  filter(variable %in% pca.vars) 
extend.num <- function(x) unlist(lapply(x, function(v) c(0, v, NA)))
extend.var <- function(x) unlist(lapply(x, function(v) c(NA, v, NA)))
d.pca.var <- data.frame(
  variable=extend.var(d.pca.var$variable),
  PC1=extend.num(d.pca.var$PC1),
  PC2=extend.num(d.pca.var$PC2)
)
# Parameters for axis with no grid lines, ticks or labels
empty.axis <- list(
  title = '',
  zeroline = FALSE,
  showline = FALSE,
  showticklabels = FALSE,
  ticklen = 0,
  showgrid = FALSE
)
# Create a line plot of each variable showing which direction it moves within our 2D space
p1 <- plot_ly(
    d.pca.var, x=~PC1, y=~PC2, text=~variable, type='scatter', 
    mode='lines+text', opacity=1, line=list(color='black'), textfont=list(color='white', size=14)
  ) %>% layout(
    xaxis=list(range = c(-.5, .5), showgrid=F, zeroline=F), 
    yaxis=list(showgrid=F, zeroline=F)
  )
# Create a heatmap of impairment incidence rate across our 2D space
d.hm <- d.pca.pred %>% 
  mutate(PC1=as.character(cut(PC1, breaks=3)), PC2=as.character(cut(PC2, breaks=3))) %>%
  group_by(PC1, PC2) %>% summarise(PCT=100*sum(response == 'Impaired')/n()) %>%
  acast(PC1 ~ PC2, value.var='PCT')
p2 <- plot_ly(z=d.hm, type='heatmap', reversescale=T) %>%
  layout(xaxis=empty.axis, yaxis=empty.axis)
# Overlay the above plots on top of one another
subplot(p2, p1, margin=-1) %>% 
  layout(
    paper_bgcolor='rgba(0,0,0,0)', plot_bgcolor='rgba(0,0,0,0)', 
    width=750, height=500,
    title='2D Projection of Correlated Features Overlayed w/ Impairment Rates'
  )

Simple Regression Models

# library(caret)
# set.seed(1)
# m <- train(X, y, method='rf', trControl=trainControl(method='cv', number=10))

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Cmd+Option+I.

When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Cmd+Shift+K to preview the HTML file).

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3IgaW5pdCwgcmVzdWx0cz0naGlkZScsIHdhcm5pbmc9RiwgbWVzc2FnZT1GLCBlcnJvcj1GLCBlY2hvPUZ9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShjb3JycGxvdCkKbGlicmFyeShrbml0cikKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KGNhcmV0KQpsaWJyYXJ5KHBsb3RseSkKbGF5b3V0IDwtIHBsb3RseTo6bGF5b3V0CgojIFBhcGVyIG9uIGRhdGE6CiMgTXVsdGlwbGV4ZWQgSW1tdW5vYXNzYXkgUGFuZWwgSWRlbnRpZmllcyBOb3ZlbCBDU0YgQmlvbWFya2VycyBmb3IgQWx6aGVpbWVyJ3MgRGlzZWFzZSBEaWFnbm9zaXMgYW5kIFByb2dub3NpcwojIGxpYnJhcnkoQXBwbGllZFByZWRpY3RpdmVNb2RlbGluZykKIyA/ZGlhZ25vc2lzCmBgYAoKYGBge3J9CiMgTG9hZCBpbiB0aGUgd2hvbGUgQWx6aGVpbWVycyBkYXRhIGZpbGUKZGF0YSA8LSByZWFkLmNzdignfi9yZXBvcy9wb3J0Zm9saW8vZGVtb25zdHJhdGl2ZS9SL2RhdGFzZXRzL2FsemhlaW1lcnMvYWx6aGVpbWVycy5jc3YnKQoKIyBSZW1vdmUgdGhpcyBmaWVsZCAuLi4gSSBmb3Jnb3QgdG8gZG8gdGhhdCB3aGVuIGNyZWF0aW5nIHRoZSBkYXRhc2V0CmRhdGEgPC0gZGF0YSAlPiUgc2VsZWN0KC1tYWxlKQoKIyBOb3JtYWxpemUgZ2VuZGVyIGxhYmVscwpzdG9waWZub3QoYWxsKCFpcy5uYShkYXRhJGdlbmRlcikpKQpub3JtYWxpemUuZ2VuZGVyIDwtIGZ1bmN0aW9uKHgpIHsKICBpcy5tYWxlIDwtIHggJT4lIHRvbG93ZXIgJT4lIHN0cl9kZXRlY3QoJ15tJykKICBpZmVsc2UoaXMubWFsZSwgJ01hbGUnLCAnRmVtYWxlJykgJT4lIGZhY3Rvcgp9CmRhdGEgPC0gZGF0YSAlPiUgbXV0YXRlKGdlbmRlcj1ub3JtYWxpemUuZ2VuZGVyKGdlbmRlcikpCgojIENvbnZlcnQgaW50ZWdlciBmaWVsZHMgdG8gbnVtZXJpYyBmb3IgdGhlIHNha2Ugb2YgY29uc2lzdGVuY3kKZGF0YSA8LSBkYXRhICU+JSBtdXRhdGVfZWFjaF8oZnVucyhhcy5udW1lcmljKSwgYygnQmV0YWNlbGx1bGluJywgJ0VvdGF4aW5fMycpKQoKaGVhZChkYXRhKQpgYGAKCmBgYHtyfQpYIDwtIGRhdGEgJT4lIHNlbGVjdCgtcmVzcG9uc2UpCnkgPC0gZGF0YVssJ3Jlc3BvbnNlJ10KdGFibGUoeSkKYGBgCgpgYGB7cn0KbmFtZXMoWCkKYGBgCgpgYGB7cn0KWCAlPiUgc2FwcGx5KGNsYXNzKSAlPiUgdGFibGUKYGBgCgpgYGB7cn0KI1ggJT4lIHNhcHBseShjbGFzcykgJT4lIC5bLiA9PSAnZmFjdG9yJ10KbmFtZXMoWClbc2FwcGx5KFgsIGNsYXNzKSA9PSAnZmFjdG9yJ10KYGBgCgojIFBhcnRpYWwgQW5hbHlzaXMKCldlIGNvdWxkIHN0YXJ0IGJ5IGxvb2tpbmcgYXQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHNvbWUgb2YgdGhlIG1vcmUgaW50dWl0aXZlIHZhcmlhYmxlcyBsaWtlIEFnZSwgR2VuZGVyLCBhbmQgR2Vub3R5cGUgYW5kIGltcGFpcm1lbnQsIHRvIHNlZSBpZiB0aGVyZSBhcmUgYW55IG9idmlvdXMgcmVsYXRpb25zaGlwcy4KCkFnZSB2cyBJbXBhaXJtZW50CgpgYGB7cn0KZGF0YSAlPiUgCiAgbXV0YXRlKGFnZV9yYW5nZT1jdXQoYWdlLCBicmVha3M9NSkpICU+JQogIGdyb3VwX2J5KGFnZV9yYW5nZSwgcmVzcG9uc2UpICU+JSB0YWxseSAlPiUgCiAgcGxvdF9seSh4PX5hZ2VfcmFuZ2UsIHk9fm4sIGNvbG9yPX5yZXNwb25zZSwgdHlwZT0nYmFyJykgJT4lCiAgcGxvdGx5OjpsYXlvdXQoaG92ZXJtb2RlPSdjbG9zZXN0JykKYGBgCgoKR2Vub3R5cGUgdnMgSW1wYWlybWVudAoKYGBge3J9CmRhdGEgJT4lIAogIGdyb3VwX2J5KEdlbm90eXBlLCByZXNwb25zZSkgJT4lIHRhbGx5ICU+JSAKICBwbG90X2x5KHg9fkdlbm90eXBlLCB5PX5uLCBjb2xvcj1+cmVzcG9uc2UsIHR5cGU9J2JhcicpICU+JQogIHBsb3RseTo6bGF5b3V0KGhvdmVybW9kZT0nY2xvc2VzdCcpCmBgYAoKCkdlbmRlciB2cyBJbXBhaXJtZW50CgpgYGB7cn0KZGF0YSAlPiUgCiAgZ3JvdXBfYnkoZ2VuZGVyLCByZXNwb25zZSkgJT4lIHRhbGx5ICU+JSAKICBwbG90X2x5KHg9fmdlbmRlciwgeT1+biwgY29sb3I9fnJlc3BvbnNlLCB0eXBlPSdiYXInKSAlPiUKICBwbG90bHk6OmxheW91dChob3Zlcm1vZGU9J2Nsb3Nlc3QnKQpgYGAKCiMgYGBge3J9CiMgZGF0YSAlPiUgcGxvdF9seSh4PX5nZW5kZXIsIHk9fmFnZSwgdHlwZT0nYm94JykKIyBgYGAKCiMjIyBQcm90ZWluIEFuYWx5c2lzCgpOb3cgd2hhdCBhYm91dCB0aGUgb3RoZXIgfjEyNSB2YXJpYWJsZXM/ICBXZSBjYW4ndCBsb29rIGF0IHRoZW0gYWxsIG9uZSBhdCBhIHRpbWUgc28gcGVyaGFwcyB0aGVyZSBpcyBhIHdheSB0byAiY29uZGVuc2UiIHRoZW0gaW50byBzb21ldGhpbmcgbW9yZSBtYW5hZ2VhYmxlOgoKCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTB9CmxpYnJhcnkoY29ycnBsb3QpCmQucGNhIDwtIFggJT4lIHNlbGVjdCgtZ2VuZGVyLCAtR2Vub3R5cGUpCmNvci5tYXQgPC0gY29ycnBsb3QoY29yKGQucGNhKSwgb3JkZXI9J2hjbHVzdCcsIHRsLmNvbD0nYmxhY2snLCB0bC5jZXg9LjUpCgojIEV4dHJhY3QgdGhlIHZhcmlhYmxlIG5hbWVzIGZyb20gdGhlIGZpZ3VyZSBiZWxvdyBzaW5jZSB0aGV5IHdpbGwgYmUgdXNlZnVsCiMgZm9yIGNyZWF0aW5nIHNpbWlsYXIgcGxvdHMgZm9yIGNvbXBhcmlzb24gKGFuZCBpdCdzIGVhc2llciB0byBjb21wYXJlIGluIHRoZSBzYW1lIG9yZGVyKQpjb3IudmFyIDwtIHJvd25hbWVzKGNvci5tYXQpCmBgYAoKCmBgYHtyfQojZC5wY2EgPC0gWCAlPiUgc2VsZWN0KC1nZW5kZXIsIC1HZW5vdHlwZSkKcGNhIDwtIHByY29tcChkLnBjYSwgc2NhbGU9VCkKYGBgCgoKPCEtLSAjIFRoZSBjb2RlIGJlbG93IHdpbGwgcGxvdCB0aGUgUENBIGxvYWRpbmdzIChpZSBwY2Ekcm90YXRpb24pIG1hdHJpeCAtLT4KPCEtLSAjIGRpcmVjdGx5IHJhdGhlciB0aGFuIGxvb2tpbmcgYXQgY29ycmVsYXRpb25zIGJldHdlZW4gb3JpZ2luYWwgYW5kIHRyYW5zZm9ybWVkIHZhcmlhYmxlcyAtLT4KPCEtLSAjICh0aG91Z2ggdGhlc2Ugc2hvdyBhYm91dCB0aGUgc2FtZSB0aGluZykgLS0+Cgo8IS0tCntyLCBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9Nn0KbGlicmFyeShyZXNoYXBlMikKZHAgPC0gcGNhJHJvdGF0aW9uWywxOjI1XVtjb3IudmFyLF0gJT4lIGFzLmRhdGEuZnJhbWUgJT4lCiAgYWRkX3Jvd25hbWVzKHZhcj0nZmVhdHVyZScpICU+JQogIG1lbHQoaWQudmFycz0nZmVhdHVyZScsIHZhcmlhYmxlLm5hbWU9J3BjJykKZHAkZmVhdHVyZSA8LSBmYWN0b3IoZHAkZmVhdHVyZSwgbGV2ZWxzPXJldihjb3IudmFyKSkKZHAgJT4lIGdncGxvdChhZXMoeD1wYywgeT1mZWF0dXJlLCBmaWxsPXZhbHVlKSkgKyBnZW9tX3RpbGUoKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudDIobG93PSdyZWQnLCBoaWdoPSdibHVlJywgbWlkPSd3aGl0ZScpCi0tPgoKYGBge3IsIGZpZy53aWR0aD00LCBmaWcuaGVpZ2h0PTEwfQpjb3JycGxvdChjb3IoZC5wY2FbLGNvci52YXJdLCBwY2EkeFssMToyNV0pLCB0bC5jb2w9J2JsYWNrJywgdGwuY2V4PS41KQpgYGAKCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQpkLnBjYS5wcmVkIDwtIGFzLmRhdGEuZnJhbWUocHJlZGljdChwY2EsIGQucGNhKVssMjozXSkKZC5wY2EucHJlZCRyZXNwb25zZSA8LSB5CgpwY2EudmFycyA8LSBjKCd0YXUnLCAnQ29ydGlzb2wnLCAnYWdlJywgJ0Fwb2xpcG9wcm90ZWluX0gnLCAnQWJfNDInKQpkLnBjYS52YXIgPC0gcGNhJHJvdGF0aW9uWywyOjNdICU+JSAKICBhcy5kYXRhLmZyYW1lICU+JSBzZXROYW1lcyguLCBjKCdQQzEnLCAnUEMyJykpICU+JQogIGFkZF9yb3duYW1lcyh2YXI9J3ZhcmlhYmxlJykgJT4lCiAgZmlsdGVyKHZhcmlhYmxlICVpbiUgcGNhLnZhcnMpIAoKZXh0ZW5kLm51bSA8LSBmdW5jdGlvbih4KSB1bmxpc3QobGFwcGx5KHgsIGZ1bmN0aW9uKHYpIGMoMCwgdiwgTkEpKSkKZXh0ZW5kLnZhciA8LSBmdW5jdGlvbih4KSB1bmxpc3QobGFwcGx5KHgsIGZ1bmN0aW9uKHYpIGMoTkEsIHYsIE5BKSkpCmQucGNhLnZhciA8LSBkYXRhLmZyYW1lKAogIHZhcmlhYmxlPWV4dGVuZC52YXIoZC5wY2EudmFyJHZhcmlhYmxlKSwKICBQQzE9ZXh0ZW5kLm51bShkLnBjYS52YXIkUEMxKSwKICBQQzI9ZXh0ZW5kLm51bShkLnBjYS52YXIkUEMyKQopCgojIFBhcmFtZXRlcnMgZm9yIGF4aXMgd2l0aCBubyBncmlkIGxpbmVzLCB0aWNrcyBvciBsYWJlbHMKZW1wdHkuYXhpcyA8LSBsaXN0KAogIHRpdGxlID0gJycsCiAgemVyb2xpbmUgPSBGQUxTRSwKICBzaG93bGluZSA9IEZBTFNFLAogIHNob3d0aWNrbGFiZWxzID0gRkFMU0UsCiAgdGlja2xlbiA9IDAsCiAgc2hvd2dyaWQgPSBGQUxTRQopCgojIENyZWF0ZSBhIGxpbmUgcGxvdCBvZiBlYWNoIHZhcmlhYmxlIHNob3dpbmcgd2hpY2ggZGlyZWN0aW9uIGl0IG1vdmVzIHdpdGhpbiBvdXIgMkQgc3BhY2UKcDEgPC0gcGxvdF9seSgKICAgIGQucGNhLnZhciwgeD1+UEMxLCB5PX5QQzIsIHRleHQ9fnZhcmlhYmxlLCB0eXBlPSdzY2F0dGVyJywgCiAgICBtb2RlPSdsaW5lcyt0ZXh0Jywgb3BhY2l0eT0xLCBsaW5lPWxpc3QoY29sb3I9J2JsYWNrJyksIHRleHRmb250PWxpc3QoY29sb3I9J3doaXRlJywgc2l6ZT0xNCkKICApICU+JSBsYXlvdXQoCiAgICB4YXhpcz1saXN0KHJhbmdlID0gYygtLjUsIC41KSwgc2hvd2dyaWQ9RiwgemVyb2xpbmU9RiksIAogICAgeWF4aXM9bGlzdChzaG93Z3JpZD1GLCB6ZXJvbGluZT1GKQogICkKCiMgQ3JlYXRlIGEgaGVhdG1hcCBvZiBpbXBhaXJtZW50IGluY2lkZW5jZSByYXRlIGFjcm9zcyBvdXIgMkQgc3BhY2UKZC5obSA8LSBkLnBjYS5wcmVkICU+JSAKICBtdXRhdGUoUEMxPWFzLmNoYXJhY3RlcihjdXQoUEMxLCBicmVha3M9MykpLCBQQzI9YXMuY2hhcmFjdGVyKGN1dChQQzIsIGJyZWFrcz0zKSkpICU+JQogIGdyb3VwX2J5KFBDMSwgUEMyKSAlPiUgc3VtbWFyaXNlKFBDVD0xMDAqc3VtKHJlc3BvbnNlID09ICdJbXBhaXJlZCcpL24oKSkgJT4lCiAgYWNhc3QoUEMxIH4gUEMyLCB2YWx1ZS52YXI9J1BDVCcpCnAyIDwtIHBsb3RfbHkoej1kLmhtLCB0eXBlPSdoZWF0bWFwJywgcmV2ZXJzZXNjYWxlPVQpICU+JQogIGxheW91dCh4YXhpcz1lbXB0eS5heGlzLCB5YXhpcz1lbXB0eS5heGlzKQoKIyBPdmVybGF5IHRoZSBhYm92ZSBwbG90cyBvbiB0b3Agb2Ygb25lIGFub3RoZXIKc3VicGxvdChwMiwgcDEsIG1hcmdpbj0tMSkgJT4lIAogIGxheW91dCgKICAgIHBhcGVyX2JnY29sb3I9J3JnYmEoMCwwLDAsMCknLCBwbG90X2JnY29sb3I9J3JnYmEoMCwwLDAsMCknLCAKICAgIHdpZHRoPTc1MCwgaGVpZ2h0PTUwMCwKICAgIHRpdGxlPScyRCBQcm9qZWN0aW9uIG9mIENvcnJlbGF0ZWQgRmVhdHVyZXMgT3ZlcmxheWVkIHcvIEltcGFpcm1lbnQgUmF0ZXMnCiAgKQpgYGAKCgojIFNpbXBsZSBSZWdyZXNzaW9uIE1vZGVscwoKYGBge3J9CgpgYGAKCgpgYGB7cn0KIyBsaWJyYXJ5KGNhcmV0KQojIHNldC5zZWVkKDEpCiMgbSA8LSB0cmFpbihYLCB5LCBtZXRob2Q9J3JmJywgdHJDb250cm9sPXRyYWluQ29udHJvbChtZXRob2Q9J2N2JywgbnVtYmVyPTEwKSkKYGBgCgpBZGQgYSBuZXcgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpJbnNlcnQgQ2h1bmsqIGJ1dHRvbiBvbiB0aGUgdG9vbGJhciBvciBieSBwcmVzc2luZyAqQ21kK09wdGlvbitJKi4KCldoZW4geW91IHNhdmUgdGhlIG5vdGVib29rLCBhbiBIVE1MIGZpbGUgY29udGFpbmluZyB0aGUgY29kZSBhbmQgb3V0cHV0IHdpbGwgYmUgc2F2ZWQgYWxvbmdzaWRlIGl0IChjbGljayB0aGUgKlByZXZpZXcqIGJ1dHRvbiBvciBwcmVzcyAqQ21kK1NoaWZ0K0sqIHRvIHByZXZpZXcgdGhlIEhUTUwgZmlsZSkuCg==